# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: leon

import logging

from coin.base.book import BookBuilderBase
from coin.base.book.aggregated_book_builder import BookUpdateSide
from coin.exchange.hitbtc_v2.kr_rest.product import HitbtcProduct


class BookEntry(object):
  def __init__(self, price, qty):
    self.price = float(price)
    self.qty = float(qty)


class BookBuilder(BookBuilderBase):
  ProductType = HitbtcProduct

  def snapshot(self, bids: list, asks: list):
    self._bid_price_map.clear()
    self._ask_price_map.clear()

    for level in bids:
      price = float(level['price'])
      qty = float(level['size'])
      if qty > 0:
        self._insert_bid(price, qty, True)

    for level in asks:
      price = float(level['price'])
      qty = float(level['size'])
      if qty > 0:
        self._insert_ask(price, qty, True)

  def update_diff(self, bids: list, asks: list):
    insert_max_bid_price = None
    insert_min_ask_price = None

    for level in asks:
      price = float(level['price'])
      qty = float(level['size'])
      if qty > 0:
        self._insert_ask(price, qty, False)
        if insert_min_ask_price is None or price < insert_min_ask_price:
          insert_min_ask_price = price
      else:
        self._delete_ask(price)

    for level in bids:
      price = float(level['price'])
      qty = float(level['size'])
      if qty > 0:
        self._insert_bid(price, qty, False)
        if insert_max_bid_price is None or price > insert_max_bid_price:
          insert_max_bid_price = price
      else:
        self._delete_bid(price)

    if (insert_max_bid_price is not None and self.ask0() is not None
        and insert_max_bid_price >= self.ask0().price):
      self._delete_ask_up_to_and_including(insert_max_bid_price)
    if (insert_min_ask_price is not None and self.bid0() is not None
        and insert_min_ask_price <= self.bid0().price):
      self._delete_bid_up_to_and_including(insert_min_ask_price)

  def _insert_bid(self, price, qty, insert_only=False):
    assert qty > 0
    entry = BookEntry(price, qty)
    if insert_only:
      assert price not in self._bid_price_map
    self._bid_price_map[price] = entry

  def _insert_ask(self, price, qty, insert_only=False):
    assert qty > 0
    entry = BookEntry(price, qty)
    if insert_only:
      assert price not in self._ask_price_map
    self._ask_price_map[price] = entry

  def _delete_bid(self, price):
    if price in self._bid_price_map:
      del self._bid_price_map[price]
    else:
      logging.debug('DEL_BID_ERROR: %s', price)

  def _delete_ask(self, price):
    if price in self._ask_price_map:
      del self._ask_price_map[price]
    else:
      logging.debug('DEL_ASK_ERROR: %s', price)

  def _delete_bid_up_to_and_including(self, price):
    elist = self._bid_price_map.get_entries(price, float('Inf'), exclude_max=False)
    for e in elist:
      self._delete_bid(e.price)

  def _delete_ask_up_to_and_including(self, price):
    elist = self._ask_price_map.get_entries(-float('Inf'), price, exclude_max=False)
    for e in elist:
      self._delete_ask(e.price)


class HitbtcBookBuilder(BookBuilder):
  ProductType = HitbtcProduct

  def __init__(self, native_symbol, book_builder_observer=None):
    super().__init__("Hitbtc", native_symbol, book_builder_observer=book_builder_observer)
